/**
 * WebServer.java
 * 2009-4-16 下午03:00:13
 *
 */
package com.esen.auwserv.test;

import java.io.*;
import java.net.*;

import com.esen.auwserv.ResponseHeader;

/**
 * @author 何江舟
 *
 * 用Java实现简单的Web服务器，用于测试HTTP的基本流程
 */
public class WebServer {

  /**
   * @param args
   */
  public static void main(String[] args) {
    int i = 1, PORT = 8080;

    ServerSocket server = null; // Web服务器套接字
    Socket client = null; // 客户端套接字，即Web浏览器

    try {
      server = new ServerSocket(PORT);

      System.out.println("Web Server is listening on port " + server.getLocalPort() + ".");

      for (;;) {
        client = server.accept(); // 接受客户机的连接请求
        new ConnectionThread(client, i).start();
        ++i;
      }
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }
}

/**
 * ConnectionThread类完成与一个Web浏览器的通信
 * @author 何江舟
 *
 */
class ConnectionThread extends Thread {

  private Socket client;

  private int counter;

  public ConnectionThread(Socket client, int counter) {
    this.client = client;
    this.counter = counter;
  }

  /**
   * 线程体，每个线程对应于一个用户请求
   */
  public void run() {
    //              String language = "Content-Language:zh-CN"; // HTTP头部语言信息，此处为中文
    //              String encoding = "Content-Encoding:UTF-8"; // HTTP头部编码信息，此处为UTF-8

    try {
      String destIp = client.getInetAddress().toString(); // 客户端IP地址
      int destPort = client.getPort(); // 客户端端口号

      System.out.println("Connection " + counter + " : connected to " + destIp + " on port "
          + destPort + ".");
      PrintStream outStream = new PrintStream(client.getOutputStream(), true); // 用PrintStream处理Socket的输出流
      BufferedReader inStream = new BufferedReader(new InputStreamReader(client.getInputStream())); // 用BufferedStream处理Socket的输入流
      String inLine = inStream.readLine(); // 读取Web浏览器提交的请求信息

      System.out.println("Received: " + inLine);

      if (getRequest(inLine)) { // 如果客户端请求为“GET”请求
        String fileName = getFileName(inLine);
        File file = new File("webapps/" + fileName);
        int len = (int) file.length();
        ResponseHeader responseHeader = new ResponseHeader(); // 创建默认的响应头部信息

        if (file.exists()) { // 如果文件存在，则将文件发送给客户端
          System.out.println(fileName + " requested.");

          String fileType = getFileType(fileName); // 获得请求文件的格式

          if (fileType != null) { // 如果文件格式不为空，则根据文件格式设置响应头部信息的MIME类型
            System.out.println("Requested File Type: " + fileType);
            responseHeader.setContentType((String) ResponseHeader.mimeType.get(fileType));
          }

          responseHeader.setContentLength(len);
          outStream.println(responseHeader.getHead() + "\n");// 将HTTP协议头部信息发送给客户端

          sendFile(outStream, file); // 发送客户端请求的文件
          outStream.flush(); // 清空缓存
        }
        else {
          String noFound = "<html><head><title>Not Found</title></head><body><h1>Error 404——file not found </h1></body></html>";

          responseHeader.setStatus("HTTP/1.1 404 no Found");
          responseHeader.setContentLength(noFound.length() + 2);
          outStream.println(responseHeader.getHead() + "\n");
          outStream.println(noFound);

          outStream.flush();
        }
      }

      long time = 1; // 延时时长

      while (time < 11100000) { // 延时
        ++time;
      }

      client.close(); // 关闭连接
    }
    catch (IOException ioe) {
      ioe.printStackTrace();
    }
  }

  /**
   * 判断请求类型，如果请求类型为“GET”，则返回true，为“POST”则返回false
   * 
   * @param s 客户端请求信息
   * @return 判断结果
   */
  public boolean getRequest(String inLine) {
    if (inLine.length() > 0) {
      if ("GET".equalsIgnoreCase(inLine.substring(0, 3))) {
        return true;
      }
    }
    return false;
  }

  /**
   * 获取要访问的文件名
   * 
   * @param inLine 客户端请求信息
   * @return 请求文件名
   */
  public String getFileName(String inLine) {
    String f = inLine.substring(inLine.indexOf(' ') + 1); // 去除请求头中紧接着GET的空格
    f = f.substring(0, f.indexOf(' '));

    try {
      if (f.charAt(0) == '/') { // 去除“GET”及其后的空格后，如果剩下的字符串是以'/'字符开头，则请求格式正确
        f = f.substring(1); // 去除客户端请求头信息中的'/'字符，剩下的就是请求的文件名
      }
    }
    catch (StringIndexOutOfBoundsException e) { // 如果出现字符串位置越界异常，则在控制台打印出异常信息，并发送一条服务器内部错误的消息给客户端
      e.printStackTrace();

      f = "error.html"; // 当服务器出现异常时，发送默认错误页面给客户端，此处为error.html
    }

    if ("".equals(f)) { // 如果用户没有指定文件名，则默认传送给用户的文件为“index.html”
      f = "index.html";
    }

    return f; // 将处理完客户端请求后的结果返回给客户端
  }

  /**
   * 将客户端请求的文件发送给客户端
   * 
   * @param outs 套接字输出流，用于将文件发送给客户端
   * @param file 客户端请求的文件，即要发送的文件
   */
  public void sendFile(PrintStream outs, File file) {
    DataInputStream in = null;
    try {
      in = new DataInputStream(new FileInputStream(file));

      // 一次性读取要发送文件的全部内容
      int len = (int) file.length();
      byte[] buf = new byte[len];
      in.readFully(buf);

      outs.write(buf, 0, len); // 发送文件
      outs.flush(); // 清空缓存
    }
    catch (Exception e) { // 当发送文件出现异常时，在控制台打印异常信息，并以状态1推出程序，表示程序非正常退出
      e.printStackTrace();
      System.exit(1);
    }
    finally { // 由于文件发送已经完毕，需要关闭服务端读取要发送文件的流，在finally块中执行，能保证流的关闭
      if (in != null) { // 如果流不为空，则关闭
        try {
          in.close();
        }
        catch (IOException ioe) {
          ioe.printStackTrace();
        }
      }
    }
  }

  /**
   * 获得文件类型，首先获得文件名中最后一个'.'字符的位置，其后的子串即为
   * 
   * @param fileName
   * @return
   */
  public String getFileType(String fileName) {
    String fileType = null; // 默认文件格式为null值
    int lastPosition = fileName.lastIndexOf("."); // 获得文件名中最后一个'.'的位置，其后的子串就是文件格式

    if (lastPosition >= 0) { // 当lastPosition大于等于零时，说明输入的文件名包含文件格式
      fileType = fileName.substring(fileName.lastIndexOf(".") + 1);
    }

    return fileType;
  }
}
